package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
)

// Мокаем интерфейс DaDataClientInterface
type MockDaDataClient struct {
	mock.Mock
}

func (m *MockDaDataClient) searchAddress(query string) ([]*Address, error) {
	args := m.Called(query)
	return args.Get(0).([]*Address), args.Error(1)
}

func (m *MockDaDataClient) geocodeAddress(lat, lng string) ([]*Address, error) {
	args := m.Called(lat, lng)
	return args.Get(0).([]*Address), args.Error(1)
}

// Тестируем обработчик поиска адреса
func TestHandleAddressSearch(t *testing.T) {
	mockClient := new(MockDaDataClient)

	// Задаем результат для мока
	mockClient.On("searchAddress", "Moscow").Return([]*Address{
		{Value: "Moscow, Russia", City: "Moscow"},
	}, nil)

	reqBody := `{"query":"Moscow"}`
	req := httptest.NewRequest("POST", "/api/address/search", bytes.NewBuffer([]byte(reqBody)))
	req.Header.Set("Content-Type", "application/json")
	rec := httptest.NewRecorder()

	handler := handleAddressSearch(mockClient)
	handler.ServeHTTP(rec, req)

	assert.Equal(t, http.StatusOK, rec.Code)

	var resp ResponseAddress
	err := json.NewDecoder(rec.Body).Decode(&resp)
	assert.NoError(t, err)
	assert.Len(t, resp.Addresses, 1)
	assert.Equal(t, "Moscow, Russia", resp.Addresses[0].Value)
	assert.Equal(t, "Moscow", resp.Addresses[0].City)

	mockClient.AssertExpectations(t)
}

// Тестируем обработчик геокодирования
func TestHandleAddressGeocode(t *testing.T) {
	mockClient := new(MockDaDataClient)

	// Задаем результат для мока
	mockClient.On("geocodeAddress", "55.7558", "37.6173").Return([]*Address{
		{Value: "Red Square, Moscow", City: "Moscow"},
	}, nil)

	reqBody := `{"lat":"55.7558","lng":"37.6173"}`
	req := httptest.NewRequest("POST", "/api/address/geocode", bytes.NewBuffer([]byte(reqBody)))
	req.Header.Set("Content-Type", "application/json")
	rec := httptest.NewRecorder()

	handler := handleAddressGeocode(mockClient)
	handler.ServeHTTP(rec, req)

	assert.Equal(t, http.StatusOK, rec.Code)

	var resp ResponseAddress
	err := json.NewDecoder(rec.Body).Decode(&resp)
	assert.NoError(t, err)
	assert.Len(t, resp.Addresses, 1)
	assert.Equal(t, "Red Square, Moscow", resp.Addresses[0].Value)
	assert.Equal(t, "Moscow", resp.Addresses[0].City)

	mockClient.AssertExpectations(t)
}

func TestHandleAddressSearch_Error(t *testing.T) {
	mockClient := new(MockDaDataClient)

	// Задаем ошибку для мока
	mockClient.On("searchAddress", "InvalidQuery").Return([]*Address{}, errors.New("service error"))

	reqBody := `{"query":"InvalidQuery"}`
	req := httptest.NewRequest("POST", "/api/address/search", bytes.NewBuffer([]byte(reqBody)))
	req.Header.Set("Content-Type", "application/json")
	rec := httptest.NewRecorder()

	handler := handleAddressSearch(mockClient)
	handler.ServeHTTP(rec, req)

	assert.Equal(t, http.StatusInternalServerError, rec.Code)
	mockClient.AssertExpectations(t)
}

// Тестируем метод клиента поиска адресов с фиктивным сервером
func TestSearchAddress(t *testing.T) {
	client := NewDaDataClient()

	// Создаем мок-сервер
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(`{"suggestions":[{"value":"Moscow, Russia","data":{"city":"Moscow"}}]}`))
	}))
	defer server.Close()

	client.ApiURL = server.URL + "/"

	addresses, err := client.searchAddress("Moscow")
	assert.NoError(t, err)
	assert.Len(t, addresses, 1)
	assert.Equal(t, "Moscow, Russia", addresses[0].Value)
	assert.Equal(t, "Moscow", addresses[0].City)
}

// Тестируем метод клиента геокодирования с фиктивным сервером
func TestGeocodeAddress(t *testing.T) {
	client := NewDaDataClient()

	// Создаем мок-сервер
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(`{"suggestions":[{"value":"Red Square, Moscow","data":{"city":"Moscow"}}]}`))
	}))
	defer server.Close()

	client.ApiURL = server.URL + "/"

	addresses, err := client.geocodeAddress("55.7558", "37.6173")
	assert.NoError(t, err)
	assert.Len(t, addresses, 1)
	assert.Equal(t, "Red Square, Moscow", addresses[0].Value)
	assert.Equal(t, "Moscow", addresses[0].City)
}
